package service

import (
	"errors"
	"fmt"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"path/filepath"
	"prj-layout/internal/repository/cache"
	"prj-layout/internal/repository/dao"
	"prj-layout/model"
	"prj-layout/model/dto"
	"prj-layout/pkg/config"
	"prj-layout/pkg/ctime"
	"prj-layout/pkg/orm"
	"prj-layout/pkg/util"
	"time"
)

var UserService = &userService{}

type userService struct{}

//
// Register 用户注册
// @parameter registerParam
// @return    rowsAffected 此次操作影响的行数, 注册成功, 该返回值为 1
// @return    err
//
func (userService) Register(registerParam *dto.RegisterParam) (rowsAffected int64, err error) {
	zap.S().Infof("用户注册接收到的密码为:%s\n", registerParam.Password)
	// 重命名之后的身份证正面文件名称
	frontName := fmt.Sprintf(
		"%s-front-%s%s",
		registerParam.Username,
		time.Now().Format(config.DigitDateTimeLayout),
		filepath.Ext(registerParam.CardFront.Filename),
	)

	// 身份证正面保存路径
	frontPath := config.UploadsPath + frontName
	// 保存文件
	err = util.SaveUploadedFile(registerParam.CardFront, frontPath)
	if err != nil {
		zap.S().Errorf("保存身份证正面失败: %s\n", err)
		return 0, err
	}

	// 重命名之后的身份证背面文件名称
	backName := fmt.Sprintf(
		"%s-front-%s%s",
		registerParam.Username,
		time.Now().Format(config.DigitDateTimeLayout),
		filepath.Ext(registerParam.CardBack.Filename),
	)

	// 身份证背面保存路径
	backPath := config.UploadsPath + backName
	// 保存文件
	err = util.SaveUploadedFile(registerParam.CardBack, backPath)
	if err != nil {
		zap.S().Errorf("保存身份证背面失败: %s\n", err)
		return 0, err
	}

	// 对密码进行加密
	zap.S().Infof("开始对明文密码进行加密, 明文密码为: %s\n", registerParam.Password)
	hashedPassword, err := util.GenerateFromPassword(registerParam.Password)
	zap.S().Infof("加密之后的密码为: %s\n", hashedPassword)
	if err != nil {
		zap.S().Errorf("密码加密失败: %s\n", err)
		return 0, err
	}

	// 构造 User 对象
	user := &model.User{
		Username:      registerParam.Username,
		Password:      hashedPassword,
		Sex:           registerParam.Sex,
		Tel:           registerParam.Tel,
		CardFrontPath: frontPath,
		CardBackPath:  backPath,
		CreateTime:    ctime.CTime{Time: time.Now()},
		UpdateTime:    ctime.CTime{Time: time.Now()},
	}

	// 自动事务处理
	if err = dao.DB.Transaction(func(tx *gorm.DB) error {
		var e error
		rowsAffected, e = dao.UserDao.Insert(tx, user)
		if e != nil {
			return e
		}
		wallet := &model.Wallet{
			UserID:  user.ID,
			Balance: decimal.NewFromInt(0),
		}
		if rowsAffected, e = dao.WalletDao.Insert(tx, wallet); e != nil {
			return e
		}
		return nil
	}); err != nil {
		return 0, err
	}
	return rowsAffected, nil
}

//
// Login 用户登录
// @parameter loginInfo 前端传递的登录信息
// @return    token 登录成功之后生成的 token
// @return    err
//
func (userService) Login(loginInfo *dto.LoginInfo) (token string, err error) {
	zap.S().Infof("用户登录前端传递过来的密码为: %s", loginInfo.Password)
	// 判断传入参数是否为空
	if loginInfo == nil {
		zap.L().Info("登录信息为空 Login 直接返回")
		return "", errors.New("登录信息为空")
	}

	// 根据用户名从数据库中查询用户
	user, err := dao.UserDao.LoadUserByUsername(dao.DB, loginInfo.Username)
	if err != nil {
		zap.S().Errorf("从数据库中查询用户名为: %s 的用户失败: %s", loginInfo.Username, err)
		return "", err
	}

	zap.S().Infof("从数据库中获取的用户密码为:%s", user.Password)
	zap.S().Infof("接收到的前端用户用户密码为:%s", loginInfo.Password)

	// 比对数据库中的密码(加密之后的)和传递过来的密码(明文密码)
	err = util.CompareHashAndPassword(user.Password, loginInfo.Password)
	if err != nil {
		zap.S().Errorf("用户名或者密码错误: %s", err)
		return "", err
	}

	// 密码比对成功之后, 生成 token 字符串
	generateToken, err := util.GenerateToken(user.Username)
	if err != nil {
		zap.S().Errorf("根据用户名生成 token 失败: %s\n", err)
		return "", err
	}

	// 登录成功之后, 将登录用户信息保存到 redis 中
	if err = cache.SaveLoginUser(user); err != nil {
		zap.S().Infof("将登录用户存放到 redis 中失败: %s", err)
	}

	return generateToken, nil
}

//
// LoadUserByUsername 从 redis 中或者数据库中加载指定用户名的用户
// @parameter username 用户名
// @return *model.User 从 redis 中或者从数据库中查询出来的用户信息
// @return error 是否有错误发生
//
func (userService) LoadUserByUsername(username string) (*model.User, error) {
	// 首先判断 redis 中是否存在, 如果存在直接返回
	user, err := cache.GetLoginUser(username)
	if err != nil {
		zap.S().Errorf("从缓存中获取用户失败: %s", err)
		// 缓存中不存在, 去数据库中查找
		userInDB, err := dao.UserDao.LoadUserByUsername(dao.DB, username)
		if err != nil {
			zap.S().Errorf("从数据库中获取用户信息失败: %s", err)
			return nil, err
		}
		return userInDB, nil
	}
	return user, nil
}
//
// Update
//  @Description:
//  @receiver userService
//  @param user
//  @return rowsAffected
//  @return err
//
func (userService) Update(registerParam *dto.RegisterParam) (rowsAffected int64, err error) {
	// 对密码进行加密
	zap.S().Infof("开始对明文密码进行加密, 明文密码为: %s\n", registerParam.Password)
	hashedPassword, err := util.GenerateFromPassword(registerParam.Password)
	zap.S().Infof("加密之后的密码为: %s\n", hashedPassword)
	if err != nil {
		zap.S().Errorf("密码加密失败: %s\n", err)
		return 0, err
	}
	user := &model.User{
		Password: hashedPassword,
	}
	 rowsAffected, err = dao.UserDao.Update(tx, user)
	if err != nil {
		return
	}
	return dao.UserDao.Update(dao.DB, user)
}

func (userService) GetAll() (userList []model.User, err error) {
	return dao.UserDao.GetAllUser(dao.DB)
}

func (userService) GetPagedUserList(pageNum int, pageSize int) *orm.Page {
	return dao.UserDao.GetPagedUserList(dao.DB, pageNum, pageSize)
}

func (receiver userService) GetUserByTime(startTime, endTime ctime.CTime) (userList []model.User, err error) {
	return dao.UserDao.GetUserByTime(dao.DB, startTime, endTime)
}
